import math
from itertools import groupby

from odoo import fields, models, api
from odoo.tools import float_compare, float_round, float_is_zero


class PlaceOrderWizard(models.TransientModel):
    _inherit = "place.order.wizard"
    # fo_line = fields.Many2many('s.forecast.mo.line', string='Forecast MO Lines', readonly=True)
    tips = fields.Text(string='Tips')
    use_fo = fields.Boolean(string='Use Forecast Order', default=False)

    def confirmation(self):
        res = super(PlaceOrderWizard, self).confirmation()
        if self.use_fo:
            fo_line = self.get_fo_line(res.get('res_id'))
            if fo_line:
                self.env['sale.order'].browse(res.get('res_id')).sb_fo_line_ids = fo_line.ids
        return res

    @api.onchange('mpn_id')
    @api.depends('mpn_id')
    def _fo_line_value(self):
        """搜索符合条件的 forecast 行"""
        if self.mpn_id and self.order_type == 'repeat':
            all_product_tmpl_ids = self.env['product.template'].search([
                ('serial_number', '=', self.mpn_id.serial_number),
                ('categ_id.code', '=', 'CHP')
            ])
            candidates = self.env['s.forecast.order.line'].search([
                ('engineering_confirmed_product_id', 'in', all_product_tmpl_ids.ids),
                ('forecast_order_id.state', 'not in', ['engineering_confirmed', 'marketing_confirmed', 'draft', 'cancel']),
                ('remain_qty', '>', 0)
            ], order='remain_qty asc')

            if candidates:
                lines_info = '\n'.join(
                    f"  • {rec.forecast_order_id.name}  (Remain {rec.remain_qty:g})"
                    for rec in candidates
                )
                self.tips = (
                    f"Found {len(candidates)} eligible forecast lines; "
                    f"they will be automatically allocated according to the remaining quantity.😋\n"
                    f"{lines_info}"
                )
                self.use_fo = True
            else:
                self.use_fo = False
                self.tips = None

    def get_fo_line(self, so_id):
        """返回：所有剩余>0 且状态符合的 forecast 行，按剩余数量升序，最后一条可超分配"""
        self.ensure_one()
        so = self.env['sale.order'].browse(so_id)
        to_alloc = so.first_line.product_uom_qty
        # pn = so.first_line.s_customer_item_code
        all_product_tmpl_ids = self.env['product.template'].search([
            ('serial_number', '=', so.product_id.serial_number),
            ('categ_id.code', '=', 'CHP')
        ])
        candidates = self.env['s.forecast.order.line'].search([
            ('engineering_confirmed_product_id', 'in', all_product_tmpl_ids.ids),
            ('forecast_order_id.state', 'not in', ['engineering_confirmed', 'marketing_confirmed', 'draft', 'cancel']),
            ('remain_qty', '>', 0)
        ], order='remain_qty asc')
        used_line_ids = self.env['s.forecast.order.line']
        if candidates:
            allocated = 0
            ForecastMOLine = self.env['s.forecast.mo.line']
            mo_lines = self.env['s.forecast.mo.line']
            for i, fo_line in enumerate(candidates):
                is_last = (i == len(candidates) - 1)
                can_take = (to_alloc - allocated) if is_last else min(fo_line.remain_qty, to_alloc - allocated)
                if can_take <= 0:
                    break
                mo_lines += ForecastMOLine.create({
                    'forecast_order_line_id': fo_line.id,
                    'sale_order_line_id': so.first_line.id,
                    'qty': can_take,
                })
                # 处理备料绑定
                product_lock_ids = self.env['s.product.lock'].search([
                    ('forecast_order_line_id', '=', fo_line.id),
                    ('active', 'in', (False, True)),
                    ('cancel', '=', False)
                ])
                if product_lock_ids:
                    line_ids = so.order_line.filtered(lambda x: x.product_id.detailed_type == 'product')
                    if can_take >= fo_line.remain_qty and line_ids:
                        sale_line = line_ids[0]
                        product_lock_ids.filtered(lambda x: not x.sale_order_line_id).update({'sale_id': so.id, 'sale_order_line_id': sale_line.id})
                        sale_line.s_pm_quantity += fo_line.pm_quantity
                    elif line_ids:
                        sale_line = line_ids[0]
                        ratio = can_take / fo_line.pm_quantity
                        for product_id, g in groupby(product_lock_ids.sorted(lambda x: x.product_id.id),
                                                     key=lambda x: x.product_id):
                            g_product_lock_ids = self.env['s.product.lock'].concat(*g)
                            total = sum(g_product_lock_ids.mapped('qty'))
                            need_qty = float_round(total * ratio, precision_rounding=g_p.uom_id.rounding)
                            for g_p in g_product_lock_ids.filtered(lambda x: not x.sale_order_line_id):
                                if float_is_zero(need_qty, precision_rounding=g_p.uom_id.rounding):
                                    continue
                                if float_compare(need_qty, g_p.qty, precision_rounding=g_p.uom_id.rounding) != -1:
                                    g_p.write({'sale_id': so.id, 'sale_order_line_id': sale_line.id})
                                    need_qty -= g_p.qty
                                else:
                                    new = g_p.copy({
                                        'sale_id': so.id,
                                        'sale_order_line_id': sale_line.id,
                                        'qty': need_qty,
                                        'purchase_id': g_p.purchase_id.id,
                                        'purchase_line_id': g_p.purchase_line_id.id,
                                        'purchase_requisition_id': g_p.purchase_requisition_id.id,
                                        'purchase_requisition_line_id': g_p.purchase_requisition_line_id.id,
                                        'prepare_qty': math.ceil(fo_line.pm_quantity * ratio)
                                    })
                                    g_p.qty -= need_qty
                                    need_qty = 0
                        sale_line.s_pm_quantity += (math.ceil(fo_line.pm_quantity * ratio))
                used_line_ids += fo_line
                allocated += can_take
                if allocated >= to_alloc:
                    break
        return used_line_ids if used_line_ids else None
